package org.example.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.metadata.IPage;
import lombok.extern.slf4j.Slf4j;
import org.example.config.RabbitMQConfig;
import org.example.constant.RedisKey;
import org.example.controller.request.TrafficPageRequest;
import org.example.controller.request.UseTrafficRequest;
import org.example.enums.BizCodeEnum;
import org.example.enums.EventMessageTypeEnum;
import org.example.enums.TaskStateEnum;
import org.example.exception.BizException;
import org.example.feign.ProductFeignService;
import org.example.feign.ShortLinkFeignService;
import org.example.interceptor.LoginInterceptor;
import org.example.manager.TrafficManager;
import org.example.manager.TrafficTaskManager;
import org.example.model.EventMessage;
import org.example.model.LoginUser;
import org.example.model.TrafficDO;
import org.example.model.TrafficTaskDO;
import org.example.service.TrafficService;
import org.example.util.JsonData;
import org.example.util.JsonUtil;
import org.example.util.TimeUtil;
import org.example.vo.ProductVO;
import org.example.vo.TrafficVO;
import org.example.vo.UseTrafficVO;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @description:
 * @author: 谢超
 * @date: 2024/9/3 16:28
 */
@Service
@Slf4j
public class TrafficServiceImpl implements TrafficService {

    @Resource
    private TrafficManager trafficManager;

    @Resource
    private ProductFeignService productFeignService;

    @Resource
    private RedisTemplate<Object, Object> redisTemplate;

    @Resource
    private TrafficTaskManager trafficTaskManager;

    @Resource
    private RabbitMQConfig rabbitMQConfig;

    @Resource
    private RabbitTemplate rabbitTemplate;

    @Resource
    private ShortLinkFeignService shortLinkFeignService;

    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public void handleTrafficMessage(EventMessage eventMessage) {
        Long accountNo = eventMessage.getAccountNo();
        String messageType = eventMessage.getEventMessageType();

        if (EventMessageTypeEnum.PRODUCT_ORDER_PAY.name().equalsIgnoreCase(messageType)) {
            // 如果订单已经支付，新增流量
            String content = eventMessage.getContent();
            Map<String, Object> orderInfoMap = JsonUtil.json2Obj(content, Map.class);

            String outTradeNo = (String)orderInfoMap.get("outTradeNo");
            Integer buyNum = (Integer)orderInfoMap.get("buyNum");
            // 商品快照
            String productStr = (String)orderInfoMap.get("product");

            ProductVO productVO = JsonUtil.json2Obj(productStr, ProductVO.class);

            log.info("商品信息:{}", productVO);

            // 计算流量包有效期，以当前时间和商品有效天数相加得出有效期
            LocalDateTime expiredDateTime = LocalDateTime.now().plusDays(productVO.getValidDay());
            // 将时间格式转为Date
            Date date = Date.from(expiredDateTime.atZone(ZoneId.systemDefault()).toInstant());

            // 构建流量包对象
            TrafficDO trafficDO = TrafficDO.builder()
                    .accountNo(accountNo)
                    .dayLimit(productVO.getDayTimes() * buyNum)
                    .dayUsed(0)
                    .totalLimit(productVO.getTotalTimes())
                    .pluginType(productVO.getPluginType())
                    .level(productVO.getLevel())
                    .productId(productVO.getId())
                    .outTradeNo(outTradeNo)
                    .expiredDate(date)
                    .build();

            trafficManager.add(trafficDO);
            log.info("消费消息，新增流量包:{}", trafficDO);

            // 新增流量包应该删除这个key
            String totalTrafficKey = String.format(RedisKey.DAY_TOTAL_TRAFFIC, accountNo);
            redisTemplate.delete(totalTrafficKey);

        } else if (EventMessageTypeEnum.TRAFFIC_INIT_FREE.name().equalsIgnoreCase(messageType)) {
            // 免费发放流量包
            Long productId = Long.valueOf(eventMessage.getBizId());
            JsonData jsonData = productFeignService.getDetail(productId);
            ProductVO productVO = jsonData.getData(new TypeReference<ProductVO>() {});

            // 构建流量包对象
            TrafficDO trafficDO = TrafficDO.builder()
                    .accountNo(accountNo)
                    .dayLimit(productVO.getDayTimes())
                    .dayUsed(0)
                    .totalLimit(productVO.getTotalTimes())
                    .pluginType(productVO.getPluginType())
                    .level(productVO.getLevel())
                    .productId(productVO.getId())
                    .outTradeNo("free_init")
                    .expiredDate(new Date())
                    .build();

            trafficManager.add(trafficDO);
        } else if (EventMessageTypeEnum.TRAFFIC_USED.name().equalsIgnoreCase(messageType)) {
            // 流量包使用, 检查是否成功使用
            // 检查task是否存在
            // 检查锻炼是否成功
            // 如果不成功，则恢复流量包
            // 删除task（也可以更新task状态，定时删除就行）

            Long trafficTaskId = Long.valueOf(eventMessage.getBizId());
            TrafficTaskDO trafficTaskDO = trafficTaskManager.findByIdAndAccountNo(trafficTaskId, accountNo);
            if (trafficTaskDO != null && trafficTaskDO.getLockState().equalsIgnoreCase(TaskStateEnum.LOCK.name())) {
                JsonData jsonData = shortLinkFeignService.check(trafficTaskDO.getBizId());

                if (jsonData.getCode() != 0) {
                    log.error("创建短链失败，流量包回滚");
                    String userDateStr = TimeUtil.format(trafficTaskDO.getGmtCreate(), "yyyy-MM-dd");

                    trafficManager.releaseUsedTimes(accountNo, trafficTaskDO.getTrafficId(), 1, userDateStr);

                    // 新增流量包应该删除这个key
                    String totalTrafficKey = String.format(RedisKey.DAY_TOTAL_TRAFFIC, accountNo);
                    redisTemplate.delete(totalTrafficKey);
                }
                // 这块也可以改为定时删除也行，而不是立即删除
                trafficTaskManager.deleteByIdAndAccountNo(trafficTaskId, accountNo);
            }
        }
    }

    @Override
    public Map<String, Object> pageAvailable(TrafficPageRequest request) {
        int size = request.getSize();
        int page = request.getPage();

        LoginUser loginUser = LoginInterceptor.threadLocal.get();

        IPage<TrafficDO> trafficDOPage = trafficManager.pageAvailable(page, size, loginUser.getAccountNo());

        // 获取流量包列表
        List<TrafficDO> records = trafficDOPage.getRecords();

        // 将DO转为给前端的VO类
        List<TrafficVO> trafficVOList = records.stream().map((item) -> beanProcess(item)).collect(Collectors.toList());

        HashMap<String, Object> pageMap = new HashMap<>(3);
        pageMap.put("total_record", trafficDOPage.getTotal());
        pageMap.put("total_page", trafficDOPage.getPages());
        pageMap.put("current_data", trafficVOList);

        return pageMap;
    }

    public TrafficVO beanProcess(TrafficDO trafficDO) {
        TrafficVO trafficVO = new TrafficVO();
        BeanUtils.copyProperties(trafficDO, trafficVO);
        return trafficVO;
    }

    @Override
    public TrafficVO detail(long trafficId) {
        LoginUser loginUser = LoginInterceptor.threadLocal.get();
        TrafficDO trafficDO = trafficManager.findByIdAndAccountNo(trafficId, loginUser.getAccountNo());
        return beanProcess(trafficDO);
    }

    @Override
    public boolean deleteExpireTraffic() {
        return trafficManager.deleteExpireTraffic();
    }

    @Override
    @Transactional
    public JsonData reduce(UseTrafficRequest trafficRequest) {
        Long accountNo = trafficRequest.getAccountNo();
        // 处理流量包，筛选出未更新流量包，当前使用的流量包
        UseTrafficVO useTraffic = processTrafficList(accountNo);

        log.info("今天可用总次数:{}", useTraffic.getDayTotalLeftTimes(), useTraffic.getCurrentTrafficDO());

        if (useTraffic.getUnUpdatedTrafficIds().size() > 0) {
            // 更新今日流量包
            trafficManager.batchUpdateUsedTimes(accountNo, useTraffic.getUnUpdatedTrafficIds());
        }

        // 先更新，在扣减当前使用的流量包
        int row = trafficManager.addDayUsedTimes(accountNo, useTraffic.getCurrentTrafficDO().getId(), 1);

        TrafficTaskDO trafficTaskDO = TrafficTaskDO.builder().accountNo(accountNo)
                .bizId(trafficRequest.getBizId())
                .useTimes(1)
                .trafficId(useTraffic.getCurrentTrafficDO().getId())
                .lockState(TaskStateEnum.LOCK.name())
                .build();
        trafficTaskManager.add(trafficTaskDO);

        if (row != 1) {
            throw new BizException(BizCodeEnum.TRAFFIC_REDUCE_FAIL);
        }

        // 往redis设置总流量包次数，锻炼服务那边递减即可；如果有新增流量包，则删除这个key
        Integer leftSeconds = TimeUtil.getRemainSecondsOneDay(new Date());
        String totalTrafficKey = String.format(RedisKey.DAY_TOTAL_TRAFFIC, accountNo);
        redisTemplate.opsForValue().set(totalTrafficKey, useTraffic.getDayTotalLeftTimes() - 1, leftSeconds, TimeUnit.SECONDS);

        EventMessage trafficEventMessage = EventMessage.builder().accountNo(accountNo).bizId(trafficTaskDO.getId() + "").eventMessageType(EventMessageTypeEnum.TRAFFIC_USED.name()).build();
        // 发送消息回滚
        rabbitTemplate.convertAndSend(rabbitMQConfig.getTrafficEventExchange(), rabbitMQConfig.getTrafficReleaseDelayRoutingKey(), trafficEventMessage);

        return JsonData.buildSuccess();
    }

    private UseTrafficVO processTrafficList(Long accountNo) {
        // 查询所有未过期的流量包
        List<TrafficDO> trafficDOList = trafficManager.selectAvailableTraffics(accountNo);
        if (trafficDOList == null || trafficDOList.size() == 0) {
            throw new BizException(BizCodeEnum.TRAFFIC_EXCEPTION);
        }

        // 天剩余可用总次数
        Integer dayTotalLeftTimes = 0;

        // 当前使用
        TrafficDO curTrafficDO = null;

        // 没过期，但是今天没更新的流量包id列表
        List<Long> unUpdatedTrafficIds = new ArrayList<>();

        // 今天日期
        String today = TimeUtil.format(LocalDateTime.now(), "yyyy-MM-dd");

        for (TrafficDO trafficDO : trafficDOList) {
            String updated = TimeUtil.format(trafficDO.getGmtModified(), "yyyy-MM-dd");
            if (today.equalsIgnoreCase(updated)) {
                // 已经更新 天剩余可用总次数 = 总次数- 已用
                int dayLeftTimes = trafficDO.getDayLimit() - trafficDO.getDayUsed();
                dayTotalLeftTimes = dayTotalLeftTimes + dayLeftTimes;

                // 选取当前使用的流量包
                if (dayLeftTimes > 0 && curTrafficDO == null) {
                    curTrafficDO = trafficDO;
                }
            } else {
                // 未更新
                dayTotalLeftTimes = dayTotalLeftTimes + trafficDO.getDayLimit();
                unUpdatedTrafficIds.add(trafficDO.getId());

                if (curTrafficDO == null) {
                    curTrafficDO = trafficDO;
                }
            }
        }

        UseTrafficVO useTrafficVO = new UseTrafficVO(dayTotalLeftTimes, curTrafficDO, unUpdatedTrafficIds);

        return useTrafficVO;
    }
}
